#include "StdAfx.h"
#include "Block.h"
#include "util.h"
#include "colors.h"

#include <iostream>

IDirect3DVertexDeclaration9* Block::blockDecl = NULL;

// constructor
Block::Block(void) :
	indices(0),  
	vertices(NULL),
	nVertices(0), 
	nQuads(0), 
	nTriangles(0),
	dimensions(1.0f, 1.0f, 1.0f),
	color(0xFF00FF00),
	bWireframe(false),
	texture(NULL)
{
	createVtxDescription();
}

// destructor
Block::~Block(void)
{
	// releae the com objects
	COM_RELEASE(this->blockDecl);
	COM_RELEASE(this->mp_VtxBuf);
	COM_RELEASE(this->mp_IndBuf);

	// free the memory
	FREE_MEMORY(vertices);
	FREE_MEMORY(indices);	
}

/******************************************************************
Descripton: creates a surface with numRows x NumCols vertices and (numRows-1) x (NumCols-1)
quads. Each quad size is dx x dz.
NOTE: The y axis represents the height and the mesh spans the x-z axis.
*******************************************************************/
Block::Block(float x, float y, float z, float len_x, float len_y, float len_z) :
	indices(0), 
	vertices(NULL),
	nVertices(0), 
	nQuads(0), 
	nTriangles(0),
	bWireframe(false)
{
	m_Pos = D3DXVECTOR3(x, y, z);
	dimensions = D3DXVECTOR3(len_x, len_y, len_z);
	
	setScale(1.0, 1.0, 1.0);
	createVtxDescription();
	setType(BLOCK_REG);
}

/******************************************************************
Descripton: creates a surface with numRows x NumCols vertices and (numRows-1) x (NumCols-1)
quads. Each quad size is dx x dz.
NOTE: The y axis represents the height and the mesh spans the x-z axis.
*******************************************************************/
Block::Block(D3DXVECTOR3 pos, D3DXVECTOR3 dim) :
	indices(0), 
	vertices(NULL),
	nVertices(0), 
	nQuads(0), 
	nTriangles(0),
	bWireframe(false)
{
	m_Pos = D3DXVECTOR3(pos.x, pos.y, pos.z);
	dimensions = D3DXVECTOR3(dim.x, dim.y, dim.z);
	
	setScale(1.0, 1.0, 1.0);
	createVtxDescription();
	setType(BLOCK_REG);
}

/******************************************************************
Descripton: Setting the block type will determine the color.
*******************************************************************/
void Block::setType(BlockType t)
{
	type = t;
	switch(t)
	{
		default:
		case BLOCK_REG:
			color = WHITE;
			break;
		case BLOCK_GOAL:
			color = RED;
			break;
		case BLOCK_PLAYER:
			color = GRAY_50;
			break;
		case BLOCK_CHASER:
			color = PURPLE;
	}

	regenerateBlock();
}

void Block::setMaterial(float r, float g, float b)
{
	ZeroMemory(&material, sizeof(D3DMATERIAL9));
	material.Diffuse.r = r;
	material.Diffuse.g = g;
	material.Diffuse.b = b;
	material.Diffuse.a = 0.5f;

	material.Specular.r = 150.0f;
	material.Specular.g = 150.0f;
	material.Specular.b = 150.0f;
	material.Power = 90.0f;
}

/******************************************************************
Generates a flat surface.
Return: 0 if success
******************************************************************/
int Block::regenerateBlock()
{	
	int rc = 0;
	nVertices = 8;
	nQuads = 6;
	nTriangles = nQuads * 2;

	// allocate memory
	vertices = (struct DefaultVertex *) malloc(sizeof(struct DefaultVertex) * nVertices);
	if (vertices == NULL)
	{
		rc = 1;
		goto err;
	}

	indices = (long *) malloc(sizeof(long) * nTriangles * 3);
	if (indices == NULL)
	{
		rc = 1;
		goto err;
	}

	// fill the vertex buffer with positions
	for (int c = 0; c < nVertices; c++) vertices[c].color = color;
	vertices[0].pos =  m_Pos;
	vertices[1].pos =  D3DXVECTOR3(m_Pos.x + dimensions.x,	m_Pos.y,				m_Pos.z);
	vertices[2].pos =  D3DXVECTOR3(m_Pos.x + dimensions.x,	m_Pos.y + dimensions.y,	m_Pos.z);
	vertices[3].pos =  D3DXVECTOR3(m_Pos.x,					m_Pos.y + dimensions.y,	m_Pos.z);
	vertices[4].pos =  D3DXVECTOR3(m_Pos.x,					m_Pos.y + dimensions.y, m_Pos.z + dimensions.z);
	vertices[5].pos =  D3DXVECTOR3(m_Pos.x,					m_Pos.y,				m_Pos.z + dimensions.z);
	vertices[6].pos =  D3DXVECTOR3(m_Pos.x + dimensions.x,	m_Pos.y,				m_Pos.z + dimensions.z);
	vertices[7].pos =  D3DXVECTOR3(m_Pos.x + dimensions.x,	m_Pos.y + dimensions.y, m_Pos.z + dimensions.z);

	// now their normals
	vertices[0].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[1].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[2].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[3].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[4].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[5].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[6].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	vertices[7].normal =  D3DXVECTOR3(0.0f, 1.0f, 0.0f);

	// now do the texture coordinates
	vertices[0].fU = 1.0f;
	vertices[0].fV = 1.0f;

	vertices[1].fU = 0.0f;
	vertices[1].fV = 1.0f;

	vertices[2].fU = 0.0f;
	vertices[2].fV = 0.0f;

	vertices[3].fU = 1.0f;
	vertices[3].fV = 0.0f;

	vertices[4].fU = 0.0f;
	vertices[4].fV = 0.0f;

	vertices[5].fU = 0.0f;
	vertices[5].fV = 1.0f;

	vertices[6].fU = 1.0f;
	vertices[6].fV = 1.0f;

	vertices[7].fU = 1.0f;
	vertices[7].fV = 0.0f;

	// fill the index buffer
	int k = 0;

	// front side quad
	indices[k++] = 0;
	indices[k++] = 1;
	indices[k++] = 2;
			
	indices[k++] = 0;
	indices[k++] = 2;
	indices[k++] = 3;

	// left side quad
	indices[k++] = 4;
	indices[k++] = 3;
	indices[k++] = 0;
			
	indices[k++] = 4;
	indices[k++] = 5;
	indices[k++] = 0;

	// top side quad
	indices[k++] = 3;
	indices[k++] = 2;
	indices[k++] = 7;
			
	indices[k++] = 3;
	indices[k++] = 4;
	indices[k++] = 7;

	// right side quad
	indices[k++] = 1;
	indices[k++] = 2;
	indices[k++] = 6;
			
	indices[k++] = 2;
	indices[k++] = 7;
	indices[k++] = 6;

	// back side quad
	indices[k++] = 7;
	indices[k++] = 6;
	indices[k++] = 5;
			
	indices[k++] = 7;
	indices[k++] = 4;
	indices[k++] = 5;

	// bottom side quad
	indices[k++] = 0;
	indices[k++] = 1;
	indices[k++] = 6;
			
	indices[k++] = 0;
	indices[k++] = 5;
	indices[k++] = 6;

	createGraphicsBuffers(); // allocate memory in the graphics card memory for the vertices and the index buffer
	setMaterial(color.r, color.g, color.b);
	if (type == BLOCK_REG)
	{
		D3DXCreateTextureFromFile(md3dDev,
								"data/texture_stone.jpg",
								&texture);
		md3dDev->SetTexture(0,texture);
	}

	else if (type == BLOCK_GOAL)
	{
		D3DXCreateTextureFromFile(md3dDev,
								"data/texture_brick.jpg",
								&texture);
		md3dDev->SetTexture(0,texture);
	}

	return(rc);
	
err:
	// clean up
	FREE_MEMORY(vertices);
	FREE_MEMORY(indices);
	return(rc);
}


/******************************************************************
Creates the required index and vertex buffers on the graphics card
Return: 0 if success
******************************************************************/
int Block::createGraphicsBuffers(void)
{
	int rc = 0;
	struct DefaultVertex *v = NULL;
	long *ind = NULL;

//#ifdef DEFAULT_SIMPLEMESH_SHADERS
//	rc =  md3dDev->CreateVertexBuffer(this->nVertices*sizeof(struct DefaultVertex), D3DUSAGE_WRITEONLY,
//		NULL, D3DPOOL_MANAGED, &this->mp_VtxBuf, NULL);
//#else 
//	rc =  md3dDev->CreateVertexBuffer(this->nVertices*sizeof(struct DefaultVertex), D3DUSAGE_WRITEONLY,
//		DEFAULT_VERTEX_FVF, D3DPOOL_MANAGED, &this->mp_VtxBuf, NULL);
//#endif

	rc =  md3dDev->CreateVertexBuffer(this->nVertices*sizeof(struct DefaultVertex), D3DUSAGE_WRITEONLY,
		DEFAULT_VERTEX_FVF, D3DPOOL_MANAGED, &this->mp_VtxBuf, NULL);

	// lock the buffer and copy the vertices (locking the entire array
	rc = mp_VtxBuf->Lock(0, 0, (void **) &v, 0);
	if (rc != D3D_OK) { goto err; }

	memcpy(v, this->vertices, nVertices*sizeof(struct DefaultVertex));
	rc = mp_VtxBuf->Unlock();
	rc = md3dDev->CreateIndexBuffer(this->nTriangles*3*sizeof(long), D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, 
		D3DPOOL_MANAGED, &this->mp_IndBuf, NULL);

	// lock the buffer and copy the vertices (locking the entire array
	rc = mp_IndBuf->Lock(0, 0, (void **) &ind, 0);
	if (rc != D3D_OK) { goto err; }

	memcpy(ind, this->indices, nTriangles*3*sizeof(long));
	rc = mp_IndBuf->Unlock();
	if (rc != D3D_OK) { goto err; }

	return 0;

err:
	return(rc);
}


/******************************************************************
Creates the vertex description interface
Return: 0 if success
******************************************************************/
int Block::createVtxDescription(void)
{
	int rc = 0;
	if (this->blockDecl == NULL) // create the vertex decalation
	{
		struct DefaultVertex v;
		D3DVERTEXELEMENT9 decl[] = 
		{{0,0,D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		 {0,0,D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		 {0,0,D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
		D3DDECL_END()};
		decl[0].Offset = (char *) &v.pos - (char *) &v;
		decl[1].Offset = (char *) &v.normal - (char *) &v;
		decl[2].Offset = (char *) &v.color - (char *) &v;
		rc = md3dDev->CreateVertexDeclaration(decl, &blockDecl);
	}
	return 0;
}

/******************************************************************
Purpose: Change the position of the surface.
Description: Set the position of hte surface.  The surface should be 
translated so the the vertex at (0,?,0) is moved to 
Return: 0 if success
******************************************************************/
void Block::setCenterPosition(D3DXVECTOR3 centrePos)
{
	// to be udpated 
	this->m_Pos = centrePos;
	this->regenerateBlock();
}

// render the block
int Block::render(int time)
{
	md3dDev->SetMaterial( &material );
	md3dDev->SetTexture(0,texture);
	md3dDev->SetStreamSource( 0, mp_VtxBuf, 0, sizeof(DefaultVertex) );
	md3dDev->SetIndices(mp_IndBuf); // set the index buffer

	//if (bWireframe)
	//{
	//	md3dDev->SetRenderState(D3DRS_COLORVERTEX, TRUE);
	//	md3dDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	//}
	//else
	//{
	//	md3dDev->SetRenderState(D3DRS_COLORVERTEX, FALSE);
	//	md3dDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	//}

	md3dDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, this->nVertices, 0, this->nTriangles);
	return 0;
}

// very simple collision using bounding boxes
bool Block::collidespoint(const D3DXVECTOR3 pt)
{
	float minX = vertices[0].pos.x;
	float minY = vertices[0].pos.y;
	float minZ = vertices[0].pos.z;
	float maxX = vertices[7].pos.x;
	float maxY = vertices[7].pos.y;
	float maxZ = vertices[7].pos.z;
	return ((pt.x >= minX && pt.y >= minY && pt.z >= minZ) && (pt.x <= maxX && pt.y <= maxY && pt.z <= maxZ));
}

const D3DXVECTOR3 Block::BottomCenterPos()
{
	D3DXVECTOR3 bottomcenter;
	float bottomcenterX1 = vertices[0].pos.x;
	float bottomcenterZ1 = vertices[0].pos.z;
	float bottomcenterX2 = vertices[7].pos.x;
	float bottomcenterZ2 = vertices[7].pos.z;
	bottomcenter.x = m_Pos.x + (bottomcenterX2 - bottomcenterX1)* 1/2;
	bottomcenter.y = m_Pos.y;
	bottomcenter.z = m_Pos.z + (bottomcenterZ2 - bottomcenterZ1)* 1/2;

	return bottomcenter;
}

/******************************************************************
Purpose: Sets the surface scaling factor
Descripton: set the position of hte surfce.  The surface should be 
translated so the the vertex at (0,?,0) is moved to 
******************************************************************/
int Block::setScale(float scaleX, float scaleY, float scaleZ)
{
	this->m_ScaleX = scaleX;
	this->m_ScaleY = scaleY;
	this->m_ScaleZ = scaleZ;
	return 0;
}